<?php
/**
 * 
 * Basic class. Provides configuration parameters, 
 * components management, components communication
 * and basic error handling.
 * 
 * @package WKernel 
 */
//TODO: setup documentor comments and license
require_once 'WKernel/errors.php';
require_once 'WKernel/Error.php';
require_once 'WKernel/Data.php';
require_once 'WKernel/VarFilter.php';
require_once 'WKernel/IRequest/Abstract.php';
class WK_App {
	private static $APPROOT     = '.';
	private static $DEBUG_MODE  = true;
	private static $LOG_ERRORS    = true;
	private static $ERRORLOG_FILE = '/error.log';
	private static $COMPONENT_AUTOLOAD = true;
	
	const NS_REQ   = 'request';
	const NS_CPAGE = 'page';
	
	private static $components  = array();
	private static $urischemata = array();
	private static $config;
	
	private static $ENV = array();
  	private static $filter;
	
	/**
	 * 
	 * Applications intialisation (parsing configuration file)
	 * 
	 * @param $config_file path to ini-file
	 */
	public static function init($app_root, $config_file) {
		self::$APPROOT = rtrim($app_root, '/') . '/';
		self::$config     = parse_ini_file($config_file, true);
		if(!empty(self::$config['WKernel'])) {
			self::$DEBUG_MODE = self::nvl(@self::$config['WKernel']['debug_mode'], self::$DEBUG_MODE);
			self::$LOG_ERRORS = self::nvl(@self::$config['WKernel']['log_errors'], self::$LOG_ERRORS);
			self::$ERRORLOG_FILE = self::nvl(@self::$config['WKernel']['errorlog_file'], self::$ERRORLOG_FILE);
			self::$COMPONENT_AUTOLOAD = self::nvl(@self::$config['WKernel']['com_autoload'], 
												   self::$COMPONENT_AUTOLOAD);
		}
    	self::$filter = new WK_VarFilter (self::$APPROOT . @self::$config['WKernel']['varfilter_ini']);
	}
	
	public static function getAppRoot() {
		return self::$APPROOT;
	}
	
	public static function logError(Exception $e){
		if(self::$LOG_ERRORS){
			$line = '[' . date('Y-m-d H:i:s') . '] ' . $e->getMessage() . "\n";
			file_put_contents(self::$ERRORLOG_FILE, $line, FILE_APPEND);
		}
	}
	
	/**
	 * 
	 * Set application enviroment variable
	 * 
	 * @param string $name variable name
	 * @param mixed $value variable value
	 * @param string $ns variable name spage, "app" by default
	 */
	public static function setVar($name, $value, $ns = 'app'){
		if(empty(self::$ENV[$ns])) {
			self::$ENV[$ns] = array();
		}
		self::$ENV[$ns][$name] = self::$filter->filter($ns, $name, $value);
	}
	
	/**
	 * 
	 * Get application enviroment variable
	 * 
	 * @param string $name variable name
	 * @param string $ns variable name spage, "app" by default
	 */
	public static function getVar($name, $ns = 'app'){
		if(empty(self::$ENV[$ns])) return null;
		return @self::$ENV[$ns][$name];
	}
	
	/**
	 * 
	 * Loading of application component
	 * 
	 * @param $name component name
	 */
	public static function loadComponent($name){		
		if(@file_get_contents($name . '/Component.php', true))
			include_once $name . '/Component.php';
		self::getComponent($name, true);
	}
	
	/**
	 * 
	 * Component registration. Used by main component class to register it's class name 
	 * 
	 * @param string $name component name
	 * @param string $class_name component class name
	 */
	public static function registerComponent($name, $class_name) {
		self::$components[$name] = $class_name;
	}
	
	/**
	 * 
	 * Uri schema registration. Used by internal request classes to register it's class name 
	 * 
	 * @param string $name URI schema name
	 * @param string $class_name internal request class name
	 */
	public static function registerUriSchema($name, $class_name){
		self::$urischemata[$name] = $class_name;
	}
	
	/**
	 * Return exemplar of internal request
	 * @param string $uri
	 * @param array $params
	 * @return WK_IRequest_Abstract
	 */
	public static function getIRequest($uri, $params = array()){
		if(!preg_match('/^([a-z]+):(.*)$/siU', $uri, $matches)) { 
			throw self::getException(WERR_INVALID_INTERNAL_URI, 'Invalid internal URI "'.$uri.'"');
		}
		$name  = $matches[1];
		$query = $matches[2];
		if(!isset(self::$urischemata[$name]) || !class_exists(self::$urischemata[$name])) {
			throw self::getException(WERR_UNKNOWN_URI_SCHEME, 'URI scheme "'.$name.'" is not registered!');
		}
		return new self::$urischemata[$name] ($query, $params);
	}

	/**
	 * 
	 * Shortcut for running internal requests
	 * 
	 * @param string $uri
	 * @param array $params
	 * @return WK_Data_Abstract
	 * @throws WK_Error
	 */
	public static function get($uri, $params = array()){
		$request = self::getIRequest($uri, $params);
		$request->run();
		if($request->isFailed()) {
			throw self::getException(
				$request->getResult()->getValue('code'), 
				$request->getResult()->getValue('message')
			);
		}
		return $request->getResult();
	}
	
	/**
	 * Shortcut for running requests with cmd: schema
	 * @param string $cmd command name <component>/<command>
	 * @param array $params command arguments
	 */
	public static function cmd($cmd, $args = array()){
		return self::get('cmd:' . $cmd, $args);
	}
	
	/**
	 * 
	 * Returns component instance
	 * 
	 * @param string $component component name
	 * @param string $ignore_al switch off component autoload
	 */
	public static function getComponent($component, $ignore_al = false) {
		if(@self::$components [$component] instanceof WK_Component) {
			return self::$components [$component];
		}		
		elseif (class_exists ( @self::$components [$component] )) {
			$comp_obj = call_user_func ( 
				array (@self::$components [$component], 'getInstance' ), 
				@self::$config[$component] 
			);
			self::$components [$component] = $comp_obj;
			return $comp_obj;
		} 
		else {
			if(self::$COMPONENT_AUTOLOAD && !$ignore_al){
				self::loadComponent($component);
				return self::getComponent($component, true);
			}
			else {
				throw self::getException (WERR_UNKNOWN_COMPONENT, 
					'Component "' . $component . '" (class "' . @self::$components [$component] . '") does not exists!'
				);
			}
		}
	}
		
	/**
	 * @ignore
	 */
	protected static function nvl ($value, $alternative){
		return !empty($value) ? $value : $alternative;
	}
	
	protected static function getException ($code, $message){
		return new WK_Error($message, $code);
	}
}
